package org.gw.ylc.base.data;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.gw.ylc.base.repositories.CommonRepository;
import org.gw.ylc.base.util.StringArrayUtil;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.Type;
import org.springframework.beans.BeanWrapperImpl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EntityPermission {
	private String entity;
	private String role;
	private Map<String, PropertyAccess> accessMap = new HashMap<String, PropertyAccess>();
	private List<QueryFilter> filters = new ArrayList<QueryFilter>();
	@JsonIgnore
	private String hql;
	@JsonIgnore
	private List<String> parameterNames = new ArrayList<>();
	private boolean canCreate;
	private boolean canRemove;

	public String getEntity() {
		return entity;
	}

	@JsonIgnore
	public String getAlias() {
		ClassMetadata metadata = CommonRepository.getInstance().getMetadata(entity);
		String alias = StringArrayUtil.lowerCaseFirst(metadata.getMappedClass().getSimpleName());
		return alias;
	}

	public void setEntity(String entity) {
		this.entity = entity;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}

	public Map<String, PropertyAccess> getAccessMap() {
		return accessMap;
	}

	public void setAccessMap(Map<String, PropertyAccess> accessMap) {
		this.accessMap = accessMap;
	}

	public List<QueryFilter> getFilters() {
		return filters;
	}

	public void setFilters(List<QueryFilter> filters) {
		this.filters = filters;
	}

	protected void buildQuery() {

	}

	protected void buildUpdate() {
		StringBuilder hql = new StringBuilder();
		ClassMetadata metadata = CommonRepository.getInstance().getMetadata(entity);
		String alias = StringArrayUtil.lowerCaseFirst(metadata.getMappedClass().getSimpleName());
		hql.append("update ").append(entity).append(" ").append(alias);
		hql.append(" set ");

		int i = 0;
		for (PropertyAccess access : accessMap.values()) {
			if (access.isWritable()) {
				if (i > 0) {
					hql.append(" , ");
				}
				String name = access.getName();
				Type type = metadata.getPropertyType(name);
				boolean entityType = type.isEntityType();
				if (entityType) {
					String sub = type.getReturnedClass().getName();
					ClassMetadata submetadata = CommonRepository.getInstance().getMetadata(sub);
					name = name + "." + submetadata.getIdentifierPropertyName();
				}
				hql.append(alias).append(".").append(name);
				hql.append("=?");
				parameterNames.add(name);
				hql.append(parameterNames.size());
				i++;
			}
		}

		hql.append(" where ").append(metadata.getIdentifierPropertyName()).append("=?");
		parameterNames.add(metadata.getIdentifierPropertyName());
		hql.append(parameterNames.size()).append(" ");
		this.hql = hql.toString();
	}

	protected void buildDelete() {

	}

	protected void buildCreate() {

	}

	protected void buildHql() {
		if (hql != null) {
			return;
		}
		synchronized (this) {
			if (hql != null) {
				return;
			}
			parameterNames = new ArrayList<String>();
			buildQuery();
			buildUpdate();
			if (canRemove) {
				buildDelete();
			}
			if (canCreate) {
				buildCreate();
			}
		}
	}

	public boolean canDelete() {
		return canRemove;
	}

	public boolean canCreate() {
		return canCreate;
	}

	public boolean isCanCreate() {
		return canCreate;
	}

	public void setCanCreate(boolean canCreate) {
		this.canCreate = canCreate;
	}

	public boolean isCanRemove() {
		return canRemove;
	}

	public void setCanRemove(boolean canRemove) {
		this.canRemove = canRemove;
	}

	public String getHql() {
		buildHql();
		return hql;
	}

	public List<String> getParameterNames() {
		buildHql();
		return parameterNames;
	}

	public String appendConditions(List<Condition> conditions, List<Object> paramList) {
		return hql;
	}

	public List<Object> getParameters(Object entity) {
		buildHql();
		List<Object> params = new ArrayList<Object>();
		BeanWrapperImpl wrapper = new BeanWrapperImpl(entity);
		for (String p : parameterNames) {
			params.add(wrapper.getPropertyValue(p));
		}
		return params;

	}

}
